﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using DotNetX;

namespace RPGX.Graphics
{
    public class HUDObject_TextArea : HUDObject
    {
        #region Const Properties

        public const int ScrollBarWidth = 10;
        public const int ScrollBarHeight = 30;

        #endregion

        #region Properties

        public override string Name
        {
            get
            {
                return "TextArea";
            }
        }

        public string Key
        {
            get { return _key; }
            set { _key = value; }
        }
        private string _key = String.Empty;

        public Vector2 ScrollPosition
        {
            get { return _scrollPosition; }
            set { _scrollPosition = value; }
        }
        private Vector2 _scrollPosition = new Vector2(0, 0);

        public HUDObject_Image ScrollBar
        {
            get { return _scrollBar; }
            set { _scrollBar = value; }
        }
        private HUDObject_Image _scrollBar = new HUDObject_Image();

        public bool IsMouseDown
        {
            get { return _isMouseDown; }
            set { _isMouseDown = value; }
        }
        private bool _isMouseDown = false;

        public Point LastMousePosition
        {
            get { return _lastMousePosition; }
            set { _lastMousePosition = value; }
        }
        private Point _lastMousePosition = new Point();

        public int ColorR
        {
            get { return _colorR; }
            set { _colorR = value; }
        }
        private int _colorR = 255;

        public int ColorG
        {
            get { return _colorG; }
            set { _colorG = value; }
        }
        private int _colorG = 255;

        public int ColorB
        {
            get { return _colorB; }
            set { _colorB = value; }
        }
        private int _colorB = 255;

        public int ColorA
        {
            get { return _colorA; }
            set { _colorA = value; }
        }
        private int _colorA = 255;

        #endregion

        #region Public Methods

        public override bool Render(GameTime time, SpriteBatch spriteBatch, Rectangle bounds)
        {
            if(String.IsNullOrEmpty(this.Key))
                return false;

            Rectangle pos = new Rectangle();
            pos.X = bounds.X + (int)this.PositionRelativeToParent.X;
            pos.Y = bounds.Y + (int)this.PositionRelativeToParent.Y;
            pos.Width = this.Width;
            pos.Height = this.Height;

            List<string> strings = this.GetMeasuredStrings();

            var stringSize = this.HUD.Text.CurrentFont.MeasureString(" ");

            float y = pos.Y + this.ScrollPosition.Y - stringSize.Y;

            foreach (var s in strings)
            {
                y += stringSize.Y;

                if (y < pos.Y)
                    continue;

                spriteBatch.DrawString(this.HUD.Text.CurrentFont, s, new Vector2(pos.X, y), Color.FromNonPremultiplied(this.ColorR, this.ColorG, this.ColorB, this.ColorA));

                if (y > pos.Y + this.Height - (stringSize.Y * 2))
                    break;
            }

            if (strings.Count * stringSize.Y > this.Height)
            {
                this.ScrollBar.Render(time, spriteBatch, pos);
            }

            return true;
        }

        public override bool FromXML(XElement element, HUD hud)
        {
            this.ChildObjects = hud.LoadChildElements(element, this);

            this.Key = element.Attribute("key").Value;

            // scroll bar
            this.ScrollBar.HUD = hud;
            this.ScrollBar.Texture = hud.Graphics.Game.Content.Load<Texture2D>(".\\HUD\\Components\\scrollbar");
            this.ScrollBar.Width = HUDObject_TextArea.ScrollBarWidth;
            this.ScrollBar.Height = HUDObject_TextArea.ScrollBarHeight;
            this.ScrollBar.PositionRelativeToParent = new Vector2(this.Width - this.ScrollBar.Width, 0);

            var colorR = element.Attribute("r");
            if (colorR != null && !String.IsNullOrEmpty(colorR.Value))
            {
                this.ColorR = colorR.Value.ToInt32OrZero();
            }

            var colorG = element.Attribute("g");
            if (colorG != null && !String.IsNullOrEmpty(colorG.Value))
            {
                this.ColorG = colorG.Value.ToInt32OrZero();
            }

            var colorB = element.Attribute("b");
            if (colorB != null && !String.IsNullOrEmpty(colorB.Value))
            {
                this.ColorB = colorB.Value.ToInt32OrZero();
            }

            var colorA = element.Attribute("a");
            if (colorA != null && !String.IsNullOrEmpty(colorA.Value))
            {
                this.ColorA = colorA.Value.ToInt32OrZero();
            }

            return true;
        }

        public override void Update(GameTime time)
        {
            if (this.IsFocused)
            {
                KeyboardState ks = Keyboard.GetState();

                float delta = 0.0f;

                if (ks.IsKeyDown(Keys.Down))
                {
                    delta = 0.1f * time.ElapsedGameTime.Milliseconds;
                }
                else if (ks.IsKeyDown(Keys.Up))
                {
                    delta = -0.1f * time.ElapsedGameTime.Milliseconds;
                }

                if (delta != 0.0f)
                {
                    this.ScrollPosition = new Vector2(this.ScrollPosition.X, this.ScrollPosition.Y + delta);
                }
            }

            this.ClampScroll();

            this.UpdateScrollBar();

            base.Update(time);
        }

        public override bool UnLoad()
        {
            return true;
        }

        #endregion

        #region Private Methods

        private List<string> GetMeasuredStrings()
        {
            Rectangle pos = new Rectangle(0, 0, this.Width, this.Height);

            string text = this.HUD.Text.GetText(this.Key);
            Vector2 stringSize = this.HUD.Text.CurrentFont.MeasureString(text);
            List<string> strings = this.MeasureText(this.HUD.Text.CurrentFont, text, pos, stringSize);

            return strings;
        }

        private float GetTextHeight()
        {
            var strings = this.GetMeasuredStrings();
            var stringSize = this.HUD.Text.CurrentFont.MeasureString(" ");

            return strings.Count * stringSize.Y;
        }

        private void ClampScroll()
        {
            // for now, ignore X, as we have no facility for horizontal scrolling

            float scrollY = this.ScrollPosition.Y;

            float textHeight = this.GetTextHeight();

            Vector2 stringSize = this.HUD.Text.CurrentFont.MeasureString(" ");

            scrollY = MathHelper.Clamp(scrollY, stringSize.Y - textHeight, 0.0f);

            this.ScrollPosition = new Vector2(this.ScrollPosition.X, scrollY);
        }

        private List<string> MeasureText(SpriteFont font, string text, Rectangle bounds, Vector2 size)
        {
            string [] strings = text.Split(' ');
            List<string> measuredStrings = new List<string>();

            float currentX = 0.0f;
            Vector2 spaceSize = font.MeasureString(" ");

            string currentLine = String.Empty;

            if (size.X <= bounds.Width)
            {
                measuredStrings.Add(text);
            }
            else
            {
                for (int idx = 0; idx < strings.Length; idx++)
                {
                    string s = strings[idx];

                    Vector2 wordSize = font.MeasureString(s);

                    if (currentX + wordSize.X + spaceSize.X <= bounds.Width || wordSize.X > bounds.Width)
                    {
                        currentLine = String.Format("{0} {1}",
                            currentLine, s);

                        currentX += wordSize.X + spaceSize.X;
                    }
                    else
                    {
                        if(idx > 0)
                            idx--;

                        measuredStrings.Add(currentLine);
                        currentX = 0;
                        currentLine = String.Empty;
                    }
                }

                if (!String.IsNullOrEmpty(currentLine))
                    measuredStrings.Add(currentLine);
            }

            return measuredStrings;
        }

        public override void OnMouseDown(HUDMouseDownEventArgs e)
        {
            base.OnMouseDown(e);

            this.IsMouseDown = true;

            this.LastMousePosition = e.MousePosition;
        }

        public override void OnMouseLeave(HUDMouseLeaveEventArgs e)
        {
            base.OnMouseLeave(e);

            this.IsMouseDown = false;
        }

        public override void OnMouseUp(HUDMouseUpEventArgs e)
        {
            base.OnMouseUp(e);

            this.IsMouseDown = false;
        }

        public override void OnMouseOver(HUDMouseOverEventArgs e)
        {
            base.OnMouseOver(e);

            if (this.IsMouseDown)
            {
                Point delta = new Point(e.MousePosition.X - this.LastMousePosition.X,
                                        e.MousePosition.Y - this.LastMousePosition.Y);

                if (delta.Y != 0)
                {
                    float scrollBarBoundsY = this.Height - HUDObject_TextArea.ScrollBarHeight;

                    float percentage = (100.0f / scrollBarBoundsY) * delta.Y;

                    var textHeight = this.GetTextHeight() + this.Height - (this.HUD.Text.CurrentFont.MeasureString(" ").Y * 2);

                    float scrollBarDeltaY = (textHeight / 100.0f) * percentage;

                    this.ScrollPosition = new Vector2(this.ScrollPosition.X, this.ScrollPosition.Y - scrollBarDeltaY);

                    this.ClampScroll();

                    this.UpdateScrollBar();
                }
            }
        }

        #endregion

        #region Private Methods

        private void UpdateScrollBar()
        {
            var textHeight = this.GetTextHeight() + this.Height - (this.HUD.Text.CurrentFont.MeasureString(" ").Y * 2);

            float percentage = (100.0f / textHeight) * Math.Abs(this.ScrollPosition.Y);

            float scrollBarBoundsY = this.Height - HUDObject_TextArea.ScrollBarHeight;

            float scrollBarDeltaY = (scrollBarBoundsY / 100.0f) * percentage;

            this.ScrollBar.PositionRelativeToParent = new Vector2(this.ScrollBar.PositionRelativeToParent.X, scrollBarDeltaY);

            if (this.ScrollBar.PositionRelativeToParent.Y < 0)
                this.ScrollBar.PositionRelativeToParent = new Vector2(this.ScrollBar.PositionRelativeToParent.X, 0);
            else if (this.ScrollBar.PositionRelativeToParent.Y > scrollBarBoundsY)
                this.ScrollBar.PositionRelativeToParent = new Vector2(this.ScrollBar.PositionRelativeToParent.X, scrollBarBoundsY);
        }

        #endregion
    }
}
